#!/usr/bin/env python
# encoding: utf-8

# @Author  : vfengcc
# @File    : atm.py
# @Time    : 2019/12/15 15:39

'''
通过Python编程完成一个银行ATM机模拟系统，具备如下功能:
(1)登陆验证：用户输入用户名密码登陆，检测用户名是否存在以及用户名密码是否匹配；用户名密码各有三次输入机会，超过三次系统退出。
(2)菜单界面：登陆成功后显示功能操作界面，输入序号选择对应功能。
(3)用户注册：用户可以输入用户名和密码创建自己的账号，并输入电话号码等信息，如果用户名存在则让用户重新输入用户名。注册后免费赠送5000元余额。
(4)账户管理：用户可以随时查看自己的账户余额。用户可以输入其他账户用户名，实现转账功能；用户名必须存在。用户也可以模拟实现存取款功能。
(5)管理员的用户名和密码可以预先初始化。
'''

import json
import datetime


class Atm:
    # 定义用到的表对应的存储文件
    TABLES = {
        'user': 'user.txt',  # 用户表：    用户名username 密码password 手机号mobile 角色(1-管理员,0-普通)role
        'account': 'account.txt',  # 资金账号表： 用户名username  可用资金balance
        'account_flow': 'account_flow.txt'  # 资金流水表： 用户名username 变动金额changemoney 变动时间changetime 备注remark
    }
    # 新注册用户赠送金额
    NEW_USER_FREE = 5000.00

    def __init__(self):
        self.__table = {'user': {}, 'account': {}, 'account_flow': {}}
        self.__load_datas()  # 加载table数据
        self.__login_info = None

    # 装饰器，鉴权
    def auth(fn):
        """
        这里看着好像有点问题，正常情况下，这里应该只是鉴权，成功正常执行，失败执行另外一个
        """
        def wrapper(self, *args, **kwargs):
            if self.__login_info == None:
                print('\033[1;31m请登录后再操作\033[0m')
                self.login()
            return fn(self, *args, **kwargs)

        return wrapper

    # 主界面
    def welcome(self):
        print("{}{:*<60}".format('**********', '【主界面】 1.用户登录，2.用户注册，3.初始化ATM系统，0-退出'))
        while True:
            s = input('请选择操作：').strip()
            if not s.isnumeric():
                print('\033[1;31m提示：输入的序号不正确.\033[0m')
            else:
                if s == '0':
                    print('欢迎下次再来！！！')
                    break
                elif s == '1':  # 用户登录
                    self.login()
                    break
                elif s == '2':  # 用户注册
                    self.register()
                    break
                elif s == '3':  # 初始化ATM系统
                    self.init_atm()
                    break
                else:
                    print('\033[1;31m提示：输入的序号不正确.\033[0m')

    # 初始化ATM系统
    def init_atm(self):
        print('\033[1;31m该操作将清除用户/资金账号/资金流水表数据，并初始化管理员账号，确认请输入y,输入其它则退出\033[0m')
        while True:
            confirm = input('>>>').strip()
            if confirm != '':
                if confirm == 'y' or confirm == 'Y':
                    self.__clear_datas()
                    print('初始化管理员账号:')
                    while True:
                        username = input('\t请输入管理员用户名:').strip()
                        password = input('\t请输入管理员密码:').strip()
                        mobile = input('\t请输入手机号：').strip()
                        if username != '' and password != '' and mobile != '':
                            if self.__adduser(username, password, mobile=mobile, role=1):
                                print('ok-管理员添加成功')
                                self.welcome()
                                break
                            else:
                                print('\t\033[1;31m提示：管理员添加失败，请返回修改.\033[0m')
                else:
                    self.welcome()
                    break

    # 登录界面
    def login(self):
        print("{}{:*<68}".format('**********', '【登录界面】 0-返回上一级'))
        total_count, err_count = 3, 0
        while True:
            username = input('请输入用户名：').strip()
            password = input('请输入密码：').strip()
            # 返回上一级
            if username == '0' or password == '0':
                self.welcome()
                break
            if username != '' and password != '':
                # 检查用户密码
                r = self.__checklogin(username, password)
                if r == 1:
                    # 类属性赋值 记录当前登录的用户信息
                    self.__login_info = self.__table['user'][username]
                    print('登录成功')
                    self.operation()
                    break
                else:
                    err_count += 1
                    if err_count >= total_count:
                        print('\033[1;31m用户名密码错误次数过多，系统已退出\033[0m')
                        break
                    print('\033[1;31m登陆失败,还剩下{}次机会\033[0m'.format(total_count - err_count))
            print('{}'.format('-' * 50))

    # 注册界面
    def register(self):
        print("{}{:*<68}".format('**********', '【注册界面】 0-返回上一级'))
        while True:
            username = input('请输入用户名：').strip()
            password = input('请输入密码：').strip()
            mobile = input('请输入手机号：').strip()
            # 返回上一级
            if username == '0' or password == '0' or mobile == '0':
                self.welcome()
                break
            if username != '' and password != '' and mobile != '':
                if self.__adduser(username, password, mobile=mobile):
                    print('注册成功')
                    self.login()  # 跳转到登录界面
                    break
                else:
                    print('\033[1;31m提示：用户名已经存在，请返回修改.\033[0m')
            print('-' * 50)

    # 退出登录
    @auth
    def logout(self):
        self.__login_info = None
        self.welcome()

    # 操作界面
    @auth
    def operation(self):
        print("{}{:*<60}".format('**********', '【操作界面】 1-查询余额，2-查询明细 3-存款，4-取款，5-转账，6-修改密码，7-退出登录'))
        fn_dict = {
            '1': self.account_balance,
            '2': self.account_flow,
            '3': self.account_in,
            '4': self.account_out,
            '5': self.account_transfer,
            '6': self.user_change_password,
            '7': self.logout,
        }
        while True:
            s = input('请选择操作：').strip()
            if s and s in fn_dict.keys():
                fn_dict[s]()
                break
            else:
                print('\033[1;31m提示：输入的序号不正确.\033[0m')

    # 查询余额
    @auth
    def account_balance(self):
        print("{}{:*<64}".format('**********', '【操作界面】>>【查询余额】'))
        print("您的余额为{:.2f}".format(self.__getbalance()))
        s = input('输入任意值返回上一级').strip()
        if s != '':
            self.operation()

    # 查看明细
    @auth
    def account_flow(self):
        print("{}{:*<64}".format('**********', '【操作界面】>>【查询明细】'))
        print("资金明细：")
        flow_list = []
        if self.__login_info['username'] in self.__table['account_flow']:
            flow_list = self.__table['account_flow'][self.__login_info['username']]
        if len(flow_list) > 0:
            for item in flow_list:
                print('\t时间:{}\t金额:{}\t备注:{}'.format(item['changetime'], item['changemoney'], item['remark']))
        else:
            print('\t没有找到相关记录')
        s = input('输入任意值返回上一级').strip()
        if s != '':
            self.operation()

    # 存款
    @auth
    def account_in(self):
        print("{}{:*<64}".format('**********', '【操作界面】>>【存款】'))
        print("存款：0-返回上一级")
        while True:
            money = input('请输入存款金额：').strip()
            if money == '0':
                self.operation()  # 返回上一级
                break
            if money.isdigit():
                money = int(money)
                if money > 0:
                    self.__addchangeflow(self.__login_info['username'], money, '存款')
                    print('存款成功，当前账号余额 {} 元'.format(self.__getbalance()))
                else:
                    print('\033[1;31m提示：金额不正确，请重写。\033[0m')
            else:
                print('\033[1;31m提示：金额不正确，请重写。\033[0m')

    # 取款
    @auth
    def account_out(self):
        print("{}{:*<64}".format('**********', '【操作界面】>>【取款】'))
        print("取款：0-返回上一级")
        while True:
            money = input('请输入取款金额：').strip()
            if money == '0':
                self.operation()  # 返回上一级
                break
            if money.isdigit():
                money = int(money)
                if money > 0:
                    balance = self.__getbalance()
                    if money <= balance:
                        self.__addchangeflow(self.__login_info['username'], -money, '取款')
                        print('取款成功，当前账号余额 {} 元'.format(self.__getbalance()))
                    else:
                        print('\033[1;31m提示：余额不足，当前可用余额 {} 元。\033[0m'.format(balance))
                else:
                    print('\033[1;31m提示：金额不正确，请重写。\033[0m')
            else:
                print('\033[1;31m提示：金额不正确，请重写。\033[0m')

    # 转账
    @auth
    def account_transfer(self):
        print("{}{:*<64}".format('**********', '【操作界面】>>【转账】'))
        print("转账：0-返回上一级")
        while True:
            touser = input('请输入收款方用户名：').strip()
            # 存在
            if touser != '' and touser in self.__table['user'].keys():
                if touser == self.__login_info['username']:
                    print('\033[1;31m提示：不能给自己转账。\033[0m')
                else:
                    while True:
                        tomoney = input('请输入转账金额：').strip()
                        if tomoney == '0':
                            self.operation()  # 返回上一级
                            break
                        if tomoney.isdigit():
                            tomoney = int(tomoney)
                            if tomoney > 0:
                                balance = self.__getbalance()
                                if tomoney <= balance:
                                    self.__addchangeflow(self.__login_info['username'], -tomoney, '转帐给 {}'.format(touser))  # 取钱
                                    self.__addchangeflow(touser, tomoney,
                                                         '来自{}'.format(self.__login_info['username']))  # 存钱
                                    print('转账成功，您的当前余额为 {} 元'.format(self.__getbalance()))
                                    self.operation()
                                    break
                                else:
                                    print('\033[1;31m提示：余额不足，当前可用余额 {} 元。\033[0m'.format(balance))
                            else:
                                print('\033[1;31m提示：转账金额输入错误.\033[0m')
                        else:
                            print('\033[1;31m提示：转账金额输入错误.\033[0m')
            else:
                print('\033[1;31m提示：收款方不存在.\033[0m')

    # 修改密码
    @auth
    def user_change_password(self):
        print("{}{:*<64}".format('**********', '【操作界面】>>【修改密码】'))
        print("修改密码：0-返回上一级")
        while True:
            oldpwd = input('请输入旧密码：').strip()
            newpwd = input('请输入新密码：').strip()
            if oldpwd == '0' or newpwd == '0':
                self.operation()  # 返回上一级
                break
            if oldpwd != '' and newpwd != '':
                if oldpwd == self.__login_info['password']:
                    self.__table['user'][self.__login_info['username']]['password'] = newpwd
                    self.__save_datas('user')
                    print('密码修改成功')
                    self.operation()
                    break
                else:
                    print('\033[1;31m提示：旧密码不正确，请重写。\033[0m')
            else:
                print('\033[1;31m提示：新旧密码不能为空，请重写。\033[0m')

    # 获取账号余额
    def __getbalance(self):
        balance = 0
        if self.__login_info['username'] in self.__table['account']:
            balance = self.__table['account'][self.__login_info['username']]['balance']
        return balance

    # 资金变动
    def __addchangeflow(self, username, money, remark=''):
        # 写入流水
        if not username in self.__table['account_flow'].keys():
            self.__table['account_flow'][username] = []
        self.__table['account_flow'][username].append({'changemoney': money,
                                                         'changetime': datetime.datetime.now().strftime(
                                                             '%Y-%m-%d %H:%M:%S'), 'remark': remark})
        self.__save_datas('account_flow')
        # 更新账户余额
        if not username in self.__table['account'].keys():
            self.__table['account'][username] = {'balance': 0}
        self.__table['account'][username]['balance'] += money
        self.__save_datas('account')

    # 检查用户名密码
    def __checklogin(self, username, password):
        if username in self.__table['user'].keys():
            if self.__table['user'][username]['password'] == password:
                return 1
            else:
                return -2
        else:
            return -1

    # 添加用户
    def __adduser(self, username, password, **kwargs):
        if username in self.__table['user'].keys():
            print('sss')
            return False
        else:
            self.__table['user'][username] = {'username': username, 'password': password, **kwargs}  # 字典修改
            self.__save_datas('user')  # 存储
            self.__addchangeflow(username, self.NEW_USER_FREE, '新用户赠送{:.2f}元余额'.format(self.NEW_USER_FREE))  # 赠送余额
            return True

    # 从文件加载数据
    def __load_datas(self):
        for table, file in self.TABLES.items():
            try:
                with open(file) as f:
                    self.__table[table] = json.load(f)
            except:
                pass

    # 将数据保存到文件
    def __save_datas(self, typ):
        if typ in self.TABLES.keys():
            with open(self.TABLES[typ], 'w+') as f:
                json.dump(self.__table[typ], f)
            return True
        else:
            return False

    # 清空表数据
    def __clear_datas(self):
        for table, file in self.TABLES.items():
            with open(file, 'w') as f:
                self.__table[table] = {}
        print('ok-用户/资金账号/资金流水表数据清除成功')

a = Atm()
a.welcome()
